from src.compiler.Parser.ast import *


class SemanticError(Exception):
    """自定义语义错误异常类"""
    pass


class SemanticAnalyzer:
    """
    语义分析器。
    通过访问者模式遍历AST，进行语义检查（如类型检查、作用域检查）
    并生成四元式中间代码。
    """

    def __init__(self):
        # 存储最终生成的四元式列表
        self.quadruples = []

        # # 符号表，用于存储数据库元信息
        # # 结构: {'table_name': {'col_name': 'TYPE', ...}}
        # self.symbol_table = {}

        # 临时变量计数器，用于生成 t0, t1, t2...
        self.temp_var_counter = 0

        # 当前作用域，主要用于在分析表达式时确定上下文（例如，当前正在查询哪张表）
        self.current_scope_table = None

    def analyze(self, ast_nodes,current_symbol_table: dict):
        """
        分析AST节点列表的公共入口。
        ast_nodes: 由语法分析器生成的一系列语句节点。
        """
        # 每次调用 analyze 时清空状态，以便分析新的SQL批处理
        self.quadruples = []
        self.temp_var_counter = 0
        self.symbol_table = current_symbol_table
        # 注意：符号表不清空，因为它模拟了数据库的持久状态

        if not ast_nodes:
            return []
        try:
            for node in ast_nodes:
                self.visit(node)
            return self.quadruples,self.symbol_table
        except SemanticError as e:
            print(f"语义错误: {e}")
            # 返回None表示分析失败
            return None,self.symbol_table

    def visit(self, node):
        """
        AST节点分发器，访问者模式的核心。
        它会根据节点类型动态调用对应的 visit_NodeType 方法。
        """
        # 例如，如果 node 是 SelectStatement 类型，就调用 self.visit_SelectStatement(node)
        method_name = f'visit_{type(node).__name__}'
        visitor = getattr(self, method_name, self.generic_visit)
        return visitor(node)

    def generic_visit(self, node):
        """
        如果某个AST节点没有专门的 visit_... 方法，则调用此方法。
        """
        raise SemanticError(f"未实现对节点 '{type(node).__name__}' 的语义分析")

    # --- 辅助方法 ---

    def new_temp(self):
        """生成一个新的、唯一的临时变量名"""
        temp_var = f"t{self.temp_var_counter}"
        self.temp_var_counter += 1
        return temp_var

    def gen_quad(self, op, arg1, arg2, result):
        """格式化并存储一个四元式"""
        quad = (str(op), str(arg1), str(arg2), str(result))
        self.quadruples.append(quad)
        # print(f"生成四元式: {quad}") # 调试时可以取消注释

    # --- 语句节点的访问者方法 ---

    def visit_CreateTableStatement(self, node: CreateTableStatement):
        table_name = node.table_name.name

        # 语义检查1: 表是否已经存在
        if table_name in self.symbol_table:
            raise SemanticError(f"表 '{table_name}' 已存在")

        self.symbol_table[table_name] = {}
        col_names = set()

        for col_def in node.columns:
            col_name, col_type = col_def
            # 语义检查2: 列名在同一个表里是否重复
            if col_name in col_names:
                raise SemanticError(f"在表 '{table_name}' 中存在重复的列名 '{col_name}'")

            col_names.add(col_name)
            # 将列信息注册到符号表
            self.symbol_table[table_name][col_name] = col_type.upper()

        # 生成四元式
        self.gen_quad('CREATE_TABLE', table_name, '_', '_')
        for col_name, col_type in node.columns:
            self.gen_quad('DEF_COLUMN', col_name, col_type, table_name)
        self.gen_quad('END_CREATE', table_name, '_', '_')

    def visit_SelectStatement(self, node: SelectStatement):
        table_name = node.from_table.name

        # 语义检查1: FROM后面的表是否存在
        if table_name not in self.symbol_table:
            raise SemanticError(f"表 '{table_name}' 不存在")

        # 设置当前作用域，以便后续的列名检查
        self.current_scope_table = table_name

        # 处理 WHERE 子句 (必须在处理 target_list 之前，因为它可能定义了后续操作需要依赖的条件)
        where_result = '_'
        if node.where_clause:
            # visit 会返回条件表达式最终结果所在的临时变量
            where_result = self.visit(node.where_clause)

        # 处理查询目标列
        processed_targets = []
        for target in node.target_list:
            if target == '*':
                # 语义检查：'*' 不能和其他列名一起使用
                if len(node.target_list) > 1:
                    raise SemanticError("SELECT * 不能与其他列名一起使用")

                # 获取表的所有列名
                all_columns = list(self.symbol_table[table_name].keys())
                processed_targets.extend(all_columns)

            elif isinstance(target, Identifier):
                # 访问标识符节点会进行列存在性检查并返回列名字符串
                processed_targets.append(self.visit(target))
            else:
                # 处理 '*' 的情况，如果它被解析为字符串
                if target == '*':
                    all_columns = list(self.symbol_table[table_name].keys())
                    processed_targets.extend(all_columns)
                else:
                    processed_targets.append(target)

        # 生成四元式
        self.gen_quad('SELECT_FROM', table_name, where_result, '_')
        for target in processed_targets:
            self.gen_quad('TARGET', target, '_', '_')
        self.gen_quad('END_SELECT', '_', '_', '_')

        # 清理当前作用域
        self.current_scope_table = None

    def visit_InsertStatement(self, node: InsertStatement):
        table_name = node.table_name.name

        # 语义检查1: 表是否存在
        if table_name not in self.symbol_table:
            raise SemanticError(f"表 '{table_name}' 不存在")

        # 语义检查2: 插入的列是否存在于表中
        table_columns = self.symbol_table[table_name]
        for col in node.column_names:
            if col.name not in table_columns:
                raise SemanticError(f"列 '{col.name}' 在表 '{table_name}' 中不存在")

        # 语义检查3: 列数量和值的数量是否匹配
        if len(node.column_names) != len(node.values):
            raise SemanticError(f"列的数量与值的数量不匹配")

        # 生成四元式
        self.gen_quad('INSERT_INTO', table_name, '_', '_')
        processed_values = [self.visit(val) for val in node.values]

        for col, val in zip(node.column_names, processed_values):
            self.gen_quad('VALUE_FOR_COL', val, col.name, '_')

        self.gen_quad('END_INSERT', '_', '_', '_')

    def visit_DeleteStatement(self, node: DeleteStatement):
        table_name = node.table_name.name

        # 语义检查1: 表是否存在
        if table_name not in self.symbol_table:
            raise SemanticError(f"表 '{table_name}' 不存在")

        self.current_scope_table = table_name

        where_result = '_'
        if node.where_clause:
            where_result = self.visit(node.where_clause)

        self.gen_quad('DELETE_FROM', table_name, where_result, '_')
        self.gen_quad('END_DELETE', '_', '_', '_')

        self.current_scope_table = None

    def visit_UpdateStatement(self, node: UpdateStatement):
        table_name = node.table_name.name

        # 语义检查1: 表必须存在
        if table_name not in self.symbol_table:
            raise SemanticError(f"表 '{table_name}' 不存在")

        # 为SET子句和WHERE子句中的列检查设置当前作用域
        self.current_scope_table = table_name

        # 优先处理 WHERE 子句
        where_result = '_'
        if node.where_clause:
            where_result = self.visit(node.where_clause)

        # 生成 UPDATE 四元式
        self.gen_quad('UPDATE', table_name, where_result, '_')

        # 遍历处理每个 SET 子句
        # *** 修改部分：UpdateStatement 的 set_clauses 是 Assignment 对象的列表 ***
        for assignment in node.set_list:
            # 现在 assignment 是一个 Assignment 对象，包含 column 和 value
            col_identifier = assignment.column
            value_expression = assignment.value
            col_name = col_identifier.name

            # 语义检查2: SET后面的列必须存在于表中
            if col_name not in self.symbol_table[table_name]:
                raise SemanticError(f"列 '{col_name}' 在表 '{table_name}' 中不存在")

            # 递归访问值表达式，获取结果 (可以是常量或临时变量)
            processed_value = self.visit(value_expression)

            # 为每个赋值生成一个 SET 四元式
            self.gen_quad('SET', processed_value, col_name, '_')

        self.gen_quad('END_UPDATE', '_', '_', '_')

        # 清理作用域
        self.current_scope_table = None

    def visit_WhereClause(self, node: WhereClause):
        """
        访问 WHERE 子句节点。
        它本身不生成四元式，而是递归访问其条件表达式，
        并将其结果（一个临时变量）返回给调用者（如 SelectStatement）。
        """
        # 递归访问条件表达式 (通常是 BinaryExpression)
        # visit_BinaryExpression 会处理具体的比较逻辑，并返回存储布尔结果的临时变量名
        print(f"--- Visiting WhereClause, processing condition: {node.condition}")
        condition_result_var = self.visit(node.condition)

        # 将条件结果返回
        return condition_result_var

    # --- 表达式节点的访问者方法 ---

    def visit_BinaryExpression(self, node: BinaryExpression):
        # 递归访问左、右操作数
        left_result = self.visit(node.left)
        right_result = self.visit(node.right)

        # TODO: 在这里可以进行详细的类型检查
        # 例如，检查 left_result 和 right_result 的类型是否与操作符 node.op 兼容

        # 创建一个临时变量来存储此二元运算的结果
        result_temp = self.new_temp()
        self.gen_quad(node.op, left_result, right_result, result_temp)

        # 返回存储结果的临时变量名，供上层表达式使用
        return result_temp

    def visit_LogicalExpression(self, node: LogicalExpression):
        # 逻辑表达式的处理方式与二元算术表达式非常相似
        left_result = self.visit(node.left)
        right_result = self.visit(node.right)
        result_temp = self.new_temp()
        self.gen_quad(node.op, left_result, right_result, result_temp)
        return result_temp

    def visit_Identifier(self, node: Identifier):
        # 语义检查: 标识符（这里通常是列名）是否存在于当前作用域的表中
        if self.current_scope_table is None:
            raise SemanticError("不能在有效作用域（如 SELECT ... FROM ...）之外使用标识符")

        table_cols = self.symbol_table.get(self.current_scope_table, {})
        if node.name not in table_cols:
            raise SemanticError(f"列 '{node.name}' 在表 '{self.current_scope_table}' 中不存在")

        # 返回标识符的名字，它将作为四元式的一个参数
        return node.name

    def visit_Constant(self, node: Constant):
        # 常量节点直接返回值本身
        return node.value

    def visit_list(self, node_list: list):
        """
        处理列表节点的访问者方法。
        当遇到一个列表时，我们只需遍历它并访问其中的每个元素。
        """
        results = []
        for item in node_list:
            results.append(self.visit(item))
        # 通常，一个语句列表的访问不返回特定值，但为了通用性，可以返回子结果列表
        return results
